package com.tomtop.core.dorequest;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.beans.BeanMap;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import com.tomtop._const.Const;
import com.tomtop.core.dorequest.HttpParamConfig.HostName;
import com.tomtop.core.utils.JSONUtils;
import com.tomtop.core.utils.MapUtils;
import com.tomtop.core.utils.ObjectUtils;

@Component
public class HttpRequestHandle {
    private final static Logger LOGGER = LoggerFactory.getLogger(HttpRequestHandle.class);
    @Autowired(required=false) RestTemplate restTemplate=new RestTemplate();

	private static String toString(Object val) {
		return val instanceof String?(String) val:JSONUtils.toJSONString(val);
	} 
	private static String encodeString(Object val) {
		try {
			return URLEncoder.encode(toString(val),Const.DEFAULT_CHARSET.name());
		} catch (UnsupportedEncodingException e) { 
			e.printStackTrace();
			return null;
		}
	} 
	private final static Pattern urlPathVariablePattern = Pattern.compile("\\{\\{([\\w _]*)\\}\\}+");//解析所有{{。。。}}标识的资源
	private static String parsePathVariable(String urlpath, Map<String, Object> path){
		Matcher a = urlPathVariablePattern.matcher(urlpath);
		Set<String> keys=new HashSet<>();
		while (a.find()){ keys.add(a.group());}; 
		String newurl = urlpath;
		for (String skey : keys) {
			String key = skey.substring(2, skey.length() - 2).trim(); 
			Object val=null;
			if (ObjectUtils.isNotNull(()->path,()->path.get(key))){
				newurl = newurl.replace(skey, encodeString(val));
			}else{
				newurl = newurl.replace(skey, "");
			} 
		} 
		return newurl;
	}
	
	private static StringBuffer parseRequestParam(Map<String, Object> param){
		StringBuffer uri = new StringBuffer(); 
		param.forEach((name,value) ->{
			uri.append( name);
			if(value!=null){
				uri.append( "=");
				uri.append( encodeString(value));
			}
			uri.append( "&");
		});
		uri.deleteCharAt(uri.length()-1); 
		return uri;
	}
	
	private static StringBuffer parseUrl(DoHttpRequestData requestData , HttpVariable variables){
		StringBuffer uri = new StringBuffer();
		String path=requestData.getPath();
		if(MapUtils.isNotEmpty(variables.getPath()))
			path=parsePathVariable(path, variables.getPath());
		uri.append(path);
		if(MapUtils.isNotEmpty(variables.getParam())){
			uri.append((path.indexOf("?") == -1)?'?':"&"); 
			uri.append(parseRequestParam(variables.getParam()));
		}
		return uri;
	} 
	
	private static HttpHeaders parseHeader(Map<String, Object> headers) { 
		HttpHeaders httpHeaders= new HttpHeaders();
		if(MapUtils.isNotEmpty(headers)){
			headers.forEach((name, value)-> {
				String valStr;
				if (value instanceof String)
					valStr=(String) value;
				else
					valStr= JSONUtils.toJSONString(value);
				httpHeaders.add(name, valStr);
			});
		} 
		return httpHeaders;
	}
	
	private static String parseCookie(Map<String, Object> cookies){
		return MapUtils.n(cookies).array((key,val)-> (key+"="+encodeString(val))).join(";"); 
	}
	
	private static String toJsonBodyString(Map<String, Object> bodys){
		if(MapUtils.isEmpty(bodys))
			return "";
		String body;
		if (bodys.size() == 1) { 
			body=toString(bodys.values().toArray()[0]);
		} else {
			body=JSONUtils.toJSONString(bodys);
		}
		return body;
	}
	@SuppressWarnings("unchecked")
	private static String toFormBodyString(Map<String, Object> bodys){
		if(MapUtils.isEmpty(bodys))
			return "";
		if (bodys.size() == 1) { 
			Object body = bodys.values().toArray()[0];
			if(! (body instanceof String))
				bodys=BeanMap.create(body);
		} 
		return MapUtils.n(bodys).array((name, val)-> name+"="+toString(val)).join("&");
	}
	
	private StringBuffer generateUrl(DoHttpRequestData requestData) {
		String hostKey = requestData.getHostKey();
		HostName hostName=HttpParamConfig.getByName(hostKey);
		StringBuffer uri=new StringBuffer()
		.append( requestData.getScheme())
		.append("://");
		if(hostName==null)
			return uri.append(hostKey);
		uri.append(hostName.getHost());
		if(hostName.getPort()>80){
			uri.append(":")
			.append(hostName.getPort());
		}
		return uri;
	}
	public HttpContext handle(DoHttpRequestData requestData, HttpVariable variables){
		StringBuffer uri=generateUrl(requestData);
		StringBuffer paramUri = parseUrl(requestData, variables);
		uri.append(paramUri);
		HttpHeaders headers=parseHeader(variables.getHeader());
		headers.add(HttpHeaders.COOKIE, parseCookie(variables.getCookie()));
		String body;
		if(requestData.isFrom()){
			body=toFormBodyString(variables.getBody());
			headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		}else{
			body=toJsonBodyString(variables.getBody());
			headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
		}
		
		URI url=URI.create(uri.toString());
		HttpMethod method=requestData.getMethod();
		RequestEntity<?> request=new RequestEntity<>(body, headers, method, url);;
		try {
			ResponseEntity<String> response = restTemplate.exchange(request, String.class);
			LOGGER.debug("请求的url {} http请求响应的对象  {}",uri,response);
			return new HttpContext(request, response);
		} catch (Exception e) {
			LOGGER.error("发请求{}, 异常消息{}", uri,e.getMessage());
			LOGGER.debug("发请求出现了异常",e);
			return new HttpContext(request, null);
		}
	}

}
